using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.Rendering;

namespace Framework
{
    public class UGUIEffectDepthAdapter : MonoBehaviour
    {
        public int Offset;
        public List<GameObject> Targets;
        private Canvas Canvas;
        private List<SortItem> _list;
        private int _executeFrame;
        public void Execute()
        {
            if (!this.enabled) return;
            var f = Time.frameCount;
            if (f == _executeFrame) return;

            Canvas = this.gameObject.GetComponent<Canvas>();
            if (Canvas == null)
            {
                Canvas = GetComponentInParent<Canvas>();
            }
            if (Canvas == null) return;
            int len;
            var sortingOrder = Canvas.sortingOrder;
            if (_executeFrame < 1)
            {
                if (_list == null)
                {
                    _list = new List<SortItem>();
                }
                else
                {
                    _list.Clear();
                }
                len = Targets.Count;
                for (int i = 0; i < len; i++)
                {
                    if (Targets[i] == null)
                    {
                        continue;
                    }
                    _list.Add(new SortItem(Targets[i]));
                }
            }
            if (_list == null) return;
            _executeFrame = f;
            var offset = sortingOrder + Offset;
            len = _list.Count;
            for (int i = 0; i < len; i++)
            {
                var item = _list[i];
                offset += item.Execute(offset);
            }
        }
        private void OnEnable()
        {
            Execute();
        }
        private class SortItem
        {
            private static readonly List<int> orderList = new List<int>();
            private static readonly Dictionary<int, int> remapOrder = new Dictionary<int, int>();
            private readonly List<Renderer> _renderers;
            private readonly List<Canvas> _canvas;
            private readonly List<int> _rendererOrders = new List<int>();
            private readonly List<int> _canvasOrders = new List<int>();
            private int _offset;
            public SortItem(GameObject target)
            {
                _offset = 0;
                _renderers = new List<Renderer>();
                _canvas = new List<Canvas>();
                var r = target.GetComponents<Renderer>();
                if (r != null && r.Length > 0)
                {
                    _renderers.AddRange(r);
                }
                r = target.GetComponentsInChildren<Renderer>(true);
                if (r != null && r.Length > 0)
                {
                    _renderers.AddRange(r);
                }
                if (_renderers.Count > 0)
                {
                    orderList.Clear();
                    remapOrder.Clear();
                    foreach (var item in _renderers)
                    {
                        var data = item.GetComponent<SortingOrderData>();
                        if (data == null)
                        {
                            data = item.gameObject.AddComponent<SortingOrderData>();
                            data.SortingOrder = item.sortingOrder;
                            data.LayerID = item.sortingLayerID;
                        }
                        item.sortingLayerID = 0;
                        item.material.renderQueue = (int)RenderQueue.Transparent;
                        if (!orderList.Contains(data.SortingOrder))
                        {
                            orderList.Add(data.SortingOrder);
                        }
                    }
                    orderList.Sort((a, b) => { return a - b; });
                    for (int i = 0; i < orderList.Count; i++)
                    {
                        remapOrder.Add(orderList[i], i);
                    }
                    _offset += orderList.Count;
                    var len = _renderers.Count;
                    for (int i = 0; i < len; i++)
                    {
                        var item = _renderers[i];
                        var order = item.sortingOrder;
                        if (remapOrder.TryGetValue(order, out int newOrder))
                        {
                            item.sortingOrder = newOrder;
                        }
                        _rendererOrders.Add(item.sortingOrder);
                        if (item.sortingOrder > _offset)
                        {
                            _offset = item.sortingOrder;
                        }
                    }
                }
                var clist = target.GetComponentsInChildren<Canvas>(true);
                if (clist != null && clist.Length > 0)
                {
                    _canvas.AddRange(clist);
                }
                clist = target.GetComponents<Canvas>();
                if (clist != null && clist.Length > 0)
                {
                    _canvas.AddRange(clist);
                }
                if (_canvas.Count > 0)
                {
                    orderList.Clear();
                    remapOrder.Clear();
                    var len = _canvas.Count;
                    for (int i = 0; i < len; i++)
                    {
                        var item = _canvas[i];
                        item.sortingLayerID = 0;
                        var data = item.GetComponent<SortingOrderData>();
                        if (data == null)
                        {
                            data = item.gameObject.AddComponent<SortingOrderData>();
                            data.SortingOrder = item.sortingOrder;
                            data.LayerID = item.sortingLayerID;
                        }
                        if (!orderList.Contains(data.SortingOrder))
                        {
                            orderList.Add(data.SortingOrder);
                        }
                    }
                    _offset += orderList.Count;
                    orderList.Sort((a, b) => { return a - b; });
                    for (int i = 0; i < orderList.Count; i++)
                    {
                        remapOrder.Add(orderList[i], i);
                    }
                    len = _canvas.Count;
                    for (int i = 0; i < len; i++)
                    {
                        var item = _canvas[i];
                        var order = item.sortingOrder;
                        if (remapOrder.TryGetValue(order, out int newOrder))
                        {
                            item.sortingOrder = newOrder;
                        }
                        _canvasOrders.Add(item.sortingOrder);
                        if (item.sortingOrder > _offset)
                        {
                            _offset = item.sortingOrder;
                        }
                    }
                }

            }
            public int Execute(int offset)
            {
                var len = _renderers.Count;
                for (int i = 0; i < len; i++)
                {
                    var item = _renderers[i];
                    if (item == null) continue;
                    item.sortingOrder = _rendererOrders[i] + offset;
                }
                len = _canvas.Count;
                for (int i = 0; i < len; i++)
                {
                    var item = _canvas[i];
                    if (item == null) continue;
                    item.sortingOrder = _canvasOrders[i] + offset;
                }
                return _offset;
            }
        }
    }
    internal class SortingOrderData : MonoBehaviour
    {
        public int LayerID;
        public int SortingOrder;
    }
}